import cv2
import numpy as np
import math


# 开闭运算的效果：
#  先腐蚀后膨胀的过程称为开运算。
# 用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。
#
#  先膨胀后腐蚀的过程称为闭运算。
# 用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积
def main():
    # 直接读取出灰度图
    mio = cv2.imread('imgs/mi.png')
    mi = cv2.cvtColor(mio, cv2.COLOR_BGR2GRAY)
    # 二值化处理
    thresh, binary_mi = cv2.threshold(mi, 0, 255, cv2.THRESH_OTSU)  # 方法选择为THRESH_OTSU
    print('阈值为：', thresh)
    cv2.imshow('binary_mi', binary_mi)
    # 去除噪声
    element = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))

    b_mi_open = cv2.morphologyEx(binary_mi, cv2.MORPH_OPEN, kernel=element)
    # cv2.imshow('b_mi_open', b_mi_open)

    # 参数详解https://blog.csdn.net/dcrmg/article/details/51987348
    # 1.获取轮廓
    image, contours, hierarchy = cv2.findContours(b_mi_open.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    count = 0  # 米的总数
    area_list = []  # 面积列表
    length_list = []  # 长度列表

    for c in contours:
        area = cv2.contourArea(c)  # 区域面积
        if area < 10:
            continue
        count += 1

        a, b, w, h = cv2.boundingRect(c)  # 计算轮廓矩形（水平的）

        # 这次是真正的米所占像素数
        area = getArea(image, a, b, w, h)
        area_list.append(area)

        # 2.获取最小包围矩形
        rect = cv2.minAreaRect(c)
        box = cv2.boxPoints(rect)
        box = np.int64(box)

        # minAreaRect函数返回矩形的中心点坐标，长宽，旋转角度[-90,0)，当矩形水平或竖直时均返回-90
        # print(rect, type(rect))
        # 中心坐标
        x, y = rect[0]
        cv2.circle(mio, (int(x), int(y)), 1, (0, 255, 0), 2)
        # 长宽,总有 width>=height
        width, height = rect[1]
        # 3. 米粒长度
        rice_length = max(width, height)
        length_list.append(rice_length)
        # 角度:[-90,0)
        angle = rect[2]

        print('blob %s: width=%s,height=%s, angle=%s rice_length=%s area=%s' % (
            count, round(width), round(height), round(angle), round(rice_length), area))  # draw contours

        # 绘制最小包围矩形
        cv2.drawContours(mio, [box], 0, (0, 0, 255), 1)
        cv2.putText(mio, '%s' % count, (a, b), cv2.FONT_HERSHEY_COMPLEX, 0.3, (0, 255, 0), 1)
        # 绘制轮廓详细信息
        cv2.drawContours(mio, c, -1, (255, 255, 0), 1)  # c是描述这个轮廓的坐标数组，-1代表所有的轮廓都绘制

    # 4.求均值
    E_area = sum(area_list) / count
    E_length = sum(length_list) / count

    # 5.求方差
    temp = 0
    for item in area_list:
        temp += math.pow(item - E_area, 2)

    D_area = temp / count

    temp = 0
    for item in length_list:
        temp += math.pow(item - E_length, 2)
    D_length = temp/count

    print('E_area=%s E_length=%s' % (round(E_area), round(E_length)))
    print('D_area=%s D_length=%s' % (round(D_area), round(D_length)))
    cv2.imshow('mio', mio)


def getArea(image, x, y, w, h):
    """
    获取图片的一个区域的图形 ,返回面积(像素值)
    image 是一个二值图，信息点是白色
    """
    zone = image[y:y + h, x:x + w]
    area = 0

    area = np.sum(zone == 255)  # 等价于后面的循环
    # for row in zone:
    #     for col in row:
    #         if col == 255:
    #             area += 1
    # print("area: ", area)
    return area


if __name__ == '__main__':
    main()
    # help(cv2.findContours)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
